package com.zhaorujie.rpc.core.remoting.params;

import com.zhaorujie.rpc.core.exception.RpcException;
import com.zhaorujie.rpc.core.remoting.invoker.RpcInvokerFactory;

import java.util.concurrent.*;

/**
 * @author 赵汝杰
 * @version v1.0
 * @description RpcFutureResponse
 * @date 2024/3/11
 **/
public class RpcFutureResponse implements Future<RpcResponse> {

    private final Object lock = new Object();
    private boolean done = false;

    private final RpcInvokerFactory rpcInvokerFactory;
    private final RpcRequest rpcRequest;
    private RpcResponse rpcResponse;

    public RpcFutureResponse(RpcRequest rpcRequest, RpcInvokerFactory rpcInvokerFactory) {
        this.rpcRequest = rpcRequest;
        this.rpcInvokerFactory = rpcInvokerFactory;

        setInvokerFuture();
    }

    public void setInvokerFuture(){
        this.rpcInvokerFactory.setInvokerFuture(rpcRequest.getRequestId(), this);
    }
    public void removeInvokerFuture(){
        this.rpcInvokerFactory.removeInvokerFuture(rpcRequest.getRequestId());
    }

    public void setRpcResponse(RpcResponse rpcResponse) {
        this.rpcResponse = rpcResponse;
        synchronized (lock) {
            this.done = true;
            lock.notifyAll();
        }
    }

    @Override
    public RpcResponse get() throws InterruptedException, ExecutionException {
        try {
            return get(-1, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            throw new RpcException(e);
        }
    }

    @Override
    public RpcResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        if (!done) {
            synchronized (lock) {
                try {
                    if (timeout < 0) {
                        lock.wait();
                    } else {
                        long timeoutMillis = (TimeUnit.MILLISECONDS==unit) ? timeout : TimeUnit.MILLISECONDS.convert(timeout, unit);
                        lock.wait(timeoutMillis);
                    }
                } catch (InterruptedException e) {
                    throw e;
                }

            }
        }
        System.out.println();
        if (!done) {
            throw new RpcException("rpc, request timeout at:"+ System.currentTimeMillis() +", request:" + rpcRequest.toString());
        }
        return rpcResponse;
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return false;
    }

    @Override
    public boolean isCancelled() {
        return false;
    }

    @Override
    public boolean isDone() {
        return done;
    }
}
